"""Support for inclusion of media files (css, javascript, etc.).

Problems:

- CSS loaded at the top of the file will not handle {% include %} properly.
- The "include" statements have to appear on every page (annoying, would prefer in the base of the template).


<!-- MEDIA render_css -->

<!-- MEDIA render_js -->

<!-- MEDIA include path/to/media path/to/media yui_component -->



"""

import os
import re

from urlparse import urljoin

from django.conf import settings
from django.utils.safestring import mark_safe

MEDIA_re = re.compile(r'<!-- MEDIA\s+(?P<command>\S+)\s*(?P<args>.*?)\s*-->', re.DOTALL)

MEDIA_TYPES = ('css', 'js')

class Media(object):

    def __init__(self):
        self.media = {}
        for media_type in MEDIA_TYPES:
            self.media[media_type] = []

    def include(self, match):
        paths = match.group('args')
        paths = re.split(r'\s+', paths)
        for path in paths:
            self.include_path(path)
        return ''

    def include_path(self, path):
        path = self.absolute_path(path)
        ext = os.path.splitext(path)[1][1:]
        try:
            what = self.media[ext]
        except KeyError:
            raise AssertionError('Unknown media type: %r' % (path,))

        if path not in what:
            what.append(path)

    def finalize(self):
        return

    def render(self, what):
        return getattr(self, 'render_%s' % (what,))()

    def render_css(self):
        links = [self.tag_css(path) for path in self.media['css']]
        return mark_safe(u'\n'.join(links))

    def render_js(self):
        scripts = [self.tag_js(path) for path in self.media['js']]
        return mark_safe(u'\n'.join(scripts))

    def tag_css(self, path):
        return u'<link rel="stylesheet" type="text/css" href="%s">' % (path,)

    def tag_js(self, path):
        return u'<script type="text/javascript" src="%s"></script>' % (path,)

    def absolute_path(self, path):
        if path.startswith(u'http://') or path.startswith(u'https://') or path.startswith(u'/'):
            return path
        return urljoin(settings.MEDIA_URL, path)


class MediaMiddleware(object):

    def media(self):
        return Media

    def process_response(self, request, response):
        m = self.media()()
        render = False
        def do_command(match):
            command = match.group('command')
            if command in MEDIA_TYPES:
                if render:
                    return m.render(command)
                else:
                    return match.group(0)
            else:
                if not render:
                    try:
                        method = getattr(m, command)
                    except AttributeError:
                        raise AssertionError('Invalid media command: %r' % (command,))
                    else:
                        method(match)

        # This has to be two pass because we need to collect all the includes
        # first before we can render (the render statement may appear before
        # the includes.
        content = MEDIA_re.sub(do_command, response.content)
        m.finalize()
        render = True
        content = MEDIA_re.sub(do_command, content)
        response.content = content
        return response
